import { nmAppErrorLog, nmAppLog } from '@app-nextmesh/common/utils';
import { LoadingButton, showLoading } from '@app-nextmesh/components';
import {
  Entity,
  FormField,
  FormNativeSelect,
} from '@app-nextmesh/components/Forms/components';
import { documentV2 } from '@app-nextmesh/domain';
import { useProjectStore } from '@app-nextmesh/store/project';
import {
  ENTITY_TYPES,
  ENTITY_TYPE_VERTEX,
  ModelEventType,
  ViewEventType,
} from '@app-nextmesh/types';
import { events } from '@app-nextmesh/utils';
import { cloneDeep, isEmpty } from 'lodash';
import { useEffect, useRef, useState } from 'react';
import BaseForm from '../../BaseForm';
import { IProps } from '../types';
import FormulaList from './FormulaList';
import ResultSelect from './ResultSelect';
import { PlotSource } from './types';

const POINT = 'point',
  AVERAGE = 'average',
  MAX = 'max',
  MIN = 'min',
  INTEGRAL = 'integral';
const TYPE_OPTIONS = [
  { label: '点计算', value: POINT },
  { label: '平均值', value: AVERAGE },
  { label: '最大值', value: MAX },
  { label: '最小值', value: MIN },
  { label: '积分', value: INTEGRAL },
];
const APPLIED_AT_OPTIONS_MAP = {
  [POINT]: [ENTITY_TYPE_VERTEX],
  [AVERAGE]: ENTITY_TYPES,
  [MAX]: ENTITY_TYPES,
  [MIN]: ENTITY_TYPES,
  [INTEGRAL]: ENTITY_TYPES,
};
interface IVisualizationProps extends IProps {
  formIndex?: number;
}
type AppliedAtData = {
  type: string;
  data: string[];
};
type DerivedFormData = {
  name: string;
  type: string;
  source: PlotSource;
  source_type: string;
  formulas: string[];
  applied_at: AppliedAtData;
};
export default function VisualizationDerivedForm({
  data: initData,
  onClose,
  onUpdate,
  onUpdateTitle,
  formIndex: initFormIndex,
}: IVisualizationProps) {
  const projectInfo = useProjectStore((state) => state.projectInfo);
  // 完整的visualization表单数据，用于提交到服务器
  const [data, setData] = useState<any>({});
  const resetTitle = useRef({ reset: true, oldTitle: '' });
  const [formData, setFormData] = useState<DerivedFormData>(null);
  const [formIndex, setFormIndex] = useState(initFormIndex);
  const [dataset, setDataset] = useState<any>(null);

  useEffect(() => {
    documentV2
      .getSources()
      .then((rspData) => {
        setDataset(rspData);
      })
      .catch((error) => {
        nmAppErrorLog('DerivedForm', 'getSources', error);
      });
  }, []);
  useEffect(() => {
    events.emit(ViewEventType.ShowBottomPanel, true, ['log', 'table']);
    return () => {
      events.emit(ViewEventType.ShowBottomPanel, false);
    };
  }, []);
  useEffect(() => {
    const newData = cloneDeep(initData.formData);
    setData(newData);
    let newForm: any = undefined;
    // 获取derived字段
    const derived = newData.derived;
    const count = (derived?.length ?? 0) + 1;
    if (initFormIndex === undefined) {
      // 添加一项新的数据
      newForm = {
        name: '派生数据集 ' + count,
        type: POINT,
        source: '',
        source_type: '',
        formulas: [''],
        applied_at: { type: ENTITY_TYPE_VERTEX, data: [] },
      };
    } else {
      newForm = cloneDeep(derived[initFormIndex]);
    }
    setFormIndex(initFormIndex);
    setFormData(newForm);
    resetTitle.current.oldTitle = newForm?.name;
    return () => {
      if (resetTitle.current.reset) {
        // 重置标题
        onUpdateTitle?.(resetTitle.current.oldTitle);
      }
    };
  }, [initData, initFormIndex]);
  useEffect(() => {
    if (!dataset) return;
    if (dataset.length === 0) return;
    let uuid = dataset[0].uuid;
    if (formData.source) {
      uuid = formData.source;
    } else {
      setFormData({ ...formData, source: uuid, source_type: dataset[0].type });
    }
    const item = dataset?.find((item) => item.uuid === uuid);
    // 加载对应的模型
    events.emit(
      ModelEventType.Loading,
      item?.type,
      item?.parent_uuid,
      item?.uuid,
    );
  }, [dataset, formData?.source]);
  const makePostData = (
    data: any,
    formData: DerivedFormData,
    formIndex: number,
  ) => {
    let derived = [];
    if (data.derived) {
      derived = data.derived;
    }
    const formulas = formData.formulas.filter((item) => !isEmpty(item));
    formData.formulas = formulas;
    if (formIndex === undefined) {
      derived.push(formData);
    } else {
      derived[formIndex] = formData;
    }
    data.derived = derived;
    return data;
  };
  const handleOk = async (
    data: any,
    formData: DerivedFormData,
    formIndex: number,
  ) => {
    const postData = makePostData(data, formData, formIndex);
    const index = formIndex ?? postData.derived.length - 1;
    const hide = showLoading();
    try {
      const newData = await documentV2.updatePostprocess(postData);
      setFormIndex(index);
      resetTitle.current.reset = false;
      onUpdate?.(newData);
      onClose?.();
    } catch (err) {
      nmAppErrorLog('DerivedForm', 'handleOk', err);
    } finally {
      hide();
    }
  };
  const handleCompute = async (
    data: any,
    formData: DerivedFormData,
    formIndex: number,
  ) => {
    nmAppLog('data', data, 'formData', formData, 'formIndex', formIndex);
    // TODO compute derived
    /**
    const postData = makePostData(data, formData, formIndex)
    const index = formIndex ?? postData.derived.length - 1
    const hide = showLoading()
    try {
       * 
      const newData = await document.compute(postData.uuid, postData.project_uuid, 'derived', index, postData)
      setFormIndex(index)
      resetTitle.current.reset = false
      onUpdate?.(newData.visualization)
      const formulas = formData.formulas
      // 处理表格数据，发送表格数据更新事件
      const results = newData.results
      const tableHeader = ["类型", "值", ...formulas]
      const tableBody = []
      for (let i = 0, l = results.length; i < l; i++) {
        tableBody.push([results[i].type, results[i].value, ...results[i].data])
      }
      events.emit(DataEventType.UpdateTable, tableHeader, tableBody)
    } finally {
      hide()
    }
*/
  };
  const handleChange = (newValue: object) => {
    setFormData((od) => {
      return { ...od, ...newValue };
    });
  };
  const handleUpdateTitle = (title: string) => {
    setFormData({ ...formData, name: title });
    onUpdateTitle?.(title);
  };
  function handleClose() {
    onClose();
  }
  if (!formData) return null;
  return (
    <BaseForm
      disabled={projectInfo.readonly}
      title={formData.name}
      onClose={handleClose}
      onOk={() => handleOk(data, formData, formIndex)}
      updateTitle={handleUpdateTitle}
      footerRight={
        <LoadingButton
          title='计算'
          onClick={async () => {
            handleCompute(data, formData, formIndex);
          }}
        />
      }>
      <FormField label={'数据源'}>
        {dataset && (
          <ResultSelect
            source={formData.source}
            onChange={(uuid) => {
              const ds = dataset.find((item) => item.uuid === uuid);
              handleChange({ source_type: ds.type, source: uuid });
            }}
          />
        )}
      </FormField>
      <FormField label={'计算类型'}>
        <FormNativeSelect
          disabled={projectInfo.readonly}
          value={formData.type}
          options={TYPE_OPTIONS}
          onChange={(value) =>
            setFormData({ ...formData, type: value as string })
          }
        />
      </FormField>
      <FormulaList
        formulas={formData.formulas}
        disabled={projectInfo.readonly}
        addable
        deletable
        onChange={(value) => handleChange({ formulas: value })}
      />
      <AppliedAt
        disabled={projectInfo.readonly}
        appliedAt={formData.applied_at}
        entityTypes={APPLIED_AT_OPTIONS_MAP[formData.type]}
        onChange={(value) => {
          handleChange({ applied_at: value });
        }}
      />
    </BaseForm>
  );
}

// 应用元素：选择类型和数据
function AppliedAt({
  disabled,
  appliedAt,
  entityTypes,
  onChange,
}: {
  disabled: boolean;
  appliedAt: AppliedAtData;
  entityTypes: string[];
  onChange: (value: AppliedAtData) => void;
}) {
  return (
    <Entity
      disabled={disabled}
      switchedOn={true}
      name='应用元素'
      type={entityTypes}
      value={appliedAt.data}
      onChange={(data) => {
        onChange({ type: appliedAt.type, data });
      }}
      assignedValues={new Set<string>()}
      onClick={() => {}}
    />
  );
}
